<?php

namespace Xin\Setting;

use Psr\SimpleCache\CacheInterface;
use Psr\SimpleCache\InvalidArgumentException;
use Xin\Capsule\Manager;
use Xin\Setting\Contracts\Factory;
use Xin\Setting\Contracts\Repository;
use Xin\Setting\Exceptions\InvalidConfigureException;

/**
 * @mixin Repository
 */
class SettingManager extends Manager implements Factory
{
	/**
	 * 默认的驱动键名
	 */
	public const DEFAULT_DRIVER_KEY = 'repository';

	/**
	 * 默认的驱动类型
	 */
	public const DEFAULT_DRIVER_VALUE = 'database';

	/**
	 * 驱动的列表键名
	 */
	public const DRIVER_CONFIG_KEY = 'repositories';

	/**
	 * @var callable
	 */
	protected $toSystemConfigResolver = null;

	/**
	 * @var CacheInterface
	 */
	protected $cache;

	/**
	 * 构造函数
	 * @param array $config
	 * @param CacheInterface $cache
	 */
	public function __construct(array $config, CacheInterface $cache)
	{
		parent::__construct($config);

		$this->cache = $cache;
	}

	/**
	 * @inheritDoc
	 */
	public function repository($name)
	{
		return $this->driver($name);
	}

	/**
	 * 创建远程驱动
	 * @param string $name
	 * @param array $config
	 * @return Repository
	 */
	protected function createRemoteDriver($name, $config)
	{
		return new RemoteRepository($config);
	}

	/**
	 * 加载配置信息
	 * @param bool $refreshCache
	 * @return array
	 * @throws InvalidArgumentException
	 */
	public function load($refreshCache = false)
	{
		return $this->fetchAndPutIntoCache(
			$this->getCacheKey(),
			[],
			$refreshCache
		);
	}

	/**
	 * 加载公开的配置信息
	 * @param bool $refreshCache
	 * @return array
	 * @throws InvalidArgumentException
	 */
	public function loadOnPublic($refreshCache = false)
	{
		return $this->fetchAndPutIntoCache(
			$this->getPublicCacheKey(),
			['public' => 1],
			$refreshCache
		);
	}

	/**
	 * 获取数据并放入缓存
	 *
	 * @param string $cacheKey
	 * @param array $search
	 * @param bool $refreshCache
	 * @return array
	 * @throws InvalidArgumentException
	 */
	protected function fetchAndPutIntoCache($cacheKey, array $search = [], $refreshCache = false)
	{
		if ($this->cache->has($cacheKey) && !$refreshCache) {
			$settings = $this->cache->get($cacheKey);
		} else {
			$items = $this->repository(null)->all($search);

			$settings = [];
			foreach ($items as $key => $item) {
				$name = $item['name'];
				if (!strpos($name, '.')) {
					$name = 'web.' . $name;
				}

				$settings[$name] = $item['value'];

				if (!empty($item['alias'])) {
					$alias = $item['alias'];
					$settings[$alias] = $settings[$name];
				}

				unset($items[$key]);
			}

			$this->cache->set($cacheKey, $settings);
		}

		return $settings;
	}

	/**
	 * 加载到系统配置里面
	 * @param bool $refreshCache
	 * @return array
	 * @throws InvalidArgumentException
	 */
	public function loadToSystemConfig($refreshCache = false)
	{
		if (!$this->toSystemConfigResolver) {
			throw new \RuntimeException("[To System Config Resolver] not defined.");
		}

		$settings = $this->load($refreshCache);

		return call_user_func($this->toSystemConfigResolver, $settings);
	}

	/**
	 * 获取加载到系统配置处理器
	 * @return callable|null
	 */
	public function getToSystemConfigResolver()
	{
		return $this->toSystemConfigResolver;
	}

	/**
	 * 设置加载到系统配置处理器
	 * @param callable|null $toSystemConfigResolver
	 * @return void
	 */
	public function setToSystemConfigResolver(callable $toSystemConfigResolver = null)
	{
		$this->toSystemConfigResolver = $toSystemConfigResolver;
	}

	/**
	 * 获取缓存的key
	 * @return string
	 */
	public function getCacheKey()
	{
		return $this->getConfig('cache.key', 'setting');
	}

	/**
	 * 获取公开的缓存key
	 * @return string
	 */
	public function getPublicCacheKey()
	{
		return $this->getConfig('cache.public_key', 'setting:public');
	}

	/**
	 * 清除缓存
	 * @return void
	 * @throws InvalidArgumentException
	 */
	public function clearCache()
	{
		$this->cache->delete($this->getCacheKey());
		$this->cache->delete($this->getPublicCacheKey());
	}

	/**
	 * 获取分组
	 *
	 * @return array
	 * @throws InvalidConfigureException
	 */
	public function getGroupList()
	{
		$groups = $this->getConfig('config_group_list', []);

		if (empty($groups)) {
			throw new InvalidConfigureException(
				"请手动配置 settings 数据表 ‘config_group_list’标识"
			);
		}

		if (!is_array($groups)) {
			throw new InvalidConfigureException(
				'获取配置分组数据格式异常！'
			);
		}

		return $groups;
	}

	/**
	 * 获取分组
	 *
	 * @return array
	 * @throws InvalidConfigureException
	 */
	public function getTypeList()
	{
		$types = $this->getConfig('config_type_list');

		if (empty($types)) {
			throw new InvalidConfigureException(
				"请手动配置数据库settings数据表 ‘config_type_list’ 标识。"
			);
		}

		return $types;
	}
}
